package com.brillilab.starter.controller.web.user;

import com.alibaba.fastjson.JSONObject;
import com.brillilab.common.constant.ClientTypeEnum;
import com.brillilab.common.constant.KeyProfixConstant;
import com.brillilab.common.constant.ResultEnum;
import com.brillilab.common.entity.ResponseVo;
import com.brillilab.common.exceptions.BrillilabException;
import com.brillilab.common.redis.RedisCache;
import com.brillilab.common.utils.CodeUtil;
import com.brillilab.common.utils.ValidatorUtil;
import com.brillilab.domain.po.lab.LabDevice;
import com.brillilab.domain.po.lab.LabMember;
import com.brillilab.domain.po.user.Users;
import com.brillilab.domain.vo.lab.LabInfoVo;
import com.brillilab.domain.vo.user.*;
import com.brillilab.service.core.lab.ILabDeviceService;
import com.brillilab.service.core.lab.ILabMemberService;
import com.brillilab.service.core.user.IUsersService;
import com.brillilab.service.logic.lab.LabMemberLogic;
import com.brillilab.service.logic.message.MessageLogic;
import com.brillilab.service.logic.user.UsersLogic;
import com.brillilab.starter.aspect.AspectLog;
import com.brillilab.starter.constant.StarterConstant;
import com.brillilab.starter.entity.req.user.UserModifyReq;
import com.brillilab.starter.entity.req.user.UserRegisterReq;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.List;


/**
 * 用户API
 *
 * @author wmh
 */
@RestController("WebUserController")
@RequestMapping("/web/user")
public class UserController {

    @Resource
    private IUsersService usersService;
    @Resource
    private UsersLogic usersLogic;
    @Resource
    private ILabDeviceService labDeviceService;
    @Resource
    private MessageLogic messageLogic;
    @Resource
    private ILabMemberService labMemberService;
    @Resource
    private LabMemberLogic labMemberLogic;
    @Resource
    private RedisCache redisCache;

    /**
     * 账号登录 +
     * {\"labId\":\"实验室ID 选填(一体机登录) L\",\"mobile\":\"用户手机号 必填 S\",\"password\":\"用户密码 必填 S\"}
     *
     * @return UserInfoVo (加入token字段,labId需要返回)
     */
    @AspectLog
    @PostMapping("/accountLogin")
    public ResponseVo userAccountLogin(@RequestBody UserLogin users) {

        Long labId = users.getLabId();
        String mobile = users.getMobile();
        String password = users.getPassword();

        Assert.isTrue(!StringUtils.isBlank(mobile),"请输入手机号！");
        Assert.isTrue(ValidatorUtil.isPhone(mobile),"手机号格式错误！");
        Assert.isTrue(!StringUtils.isBlank(password),"请输入密码！");

        UserInfoVo userInfoVo=usersLogic.userLogin(labId,mobile,password,ClientTypeEnum.WEB);

        return ResponseVo.success(userInfoVo);
    }

    /**
     * 磁卡登录 +
     * {\"labId\":\"实验室ID 必填 L\",\"cardNo\":\"磁卡号 必填 S\"}
     *
     * @return UserInfoVo (加入token字段,labId需要返回)
     */
    @PostMapping("/cardLogin")
    public ResponseVo userCardLogin(@RequestBody JSONObject json) {


        Long labId=json.getLong("labId");
        String cardNo=json.getString("cardNo");

        if(StringUtils.isBlank(cardNo)){
            return ResponseVo.failure(ResultEnum.REQUEST_PARAM_LACK);
        }

        UserInfoVo userInfoVo=usersLogic.userLogin(labId,cardNo,ClientTypeEnum.WEB);
        return ResponseVo.success(userInfoVo);
    }

    /**
     * 二维码登录 +
     * <p>
     * deviceNo 设备编号
     * qrCode   1.用户实验室ID与labId是否相同
     * 2.qrCode绑定用户信息存入缓存
     *
     * @return
     */
    @GetMapping("/qrLogin")
    public ResponseVo userQRcodeLogin(String deviceNo,String qrCode,@RequestHeader(name=StarterConstant.TOKEN) String token) {

        if(StringUtils.isBlank(token)){
            return ResponseVo.failure(ResultEnum.FAILURE,"token缺失！",null);
        }
        if(StringUtils.isBlank(deviceNo) || StringUtils.isBlank(qrCode)){
            return ResponseVo.failure(ResultEnum.REQUEST_PARAM_LACK);
        }
        LabDevice labDevice=labDeviceService.selectByNo(deviceNo);
        if(labDevice.getLabId() == null){
            return ResponseVo.failure(ResultEnum.REQUEST_PARAM_ERROR);
        }
        //获取缓存信息
        UserInfoVo userInfo=usersLogic.getUserByToken(token);

        //用户实验室ID与缓存用户信息labId是否相同，不同则不允许登录
        Assert.isTrue(userInfo.getLabId().equals(labDevice.getLabId()),"用户未切换到该实验室！");

        //qrCode绑定用户信息存入缓存
        redisCache.set(KeyProfixConstant.TOKEN + qrCode,userInfo,60 * 30);

        return ResponseVo.success(userInfo);
    }

    /**
     * 二维码登录轮询
     * <p>
     * 根据qrCode从缓存取信息
     *
     * @return UserInfoVo (加入token字段,labId需要返回)
     */
    @GetMapping("/checkQrLogin")
    public ResponseVo checkQRLogin(String qrCode) {


        if(StringUtils.isBlank(qrCode)){
            return ResponseVo.failure(ResultEnum.REQUEST_PARAM_LACK);
        }

        //获取缓存用户信息
        UserInfoVo users=(UserInfoVo) redisCache.getObj(KeyProfixConstant.TOKEN + qrCode);

        if(users == null){
            return ResponseVo.failure("用户登录信息失效！");
        }

        //返回用户信息
        UserInfoVo userInfoVo=new UserInfoVo();
        BeanUtils.copyProperties(users,userInfoVo);
        userInfoVo.setToken(qrCode);

        return ResponseVo.success(userInfoVo);
    }

    /**
     * 根据ID获取用户信息 + {\"userId\":\"用户ID 必填 L\"}
     *
     * @return
     */
    @PostMapping("/info")
    public ResponseVo userInfo(@RequestBody JSONObject json,
                               @RequestHeader(name=StarterConstant.TOKEN) String token) {
        Long userId=json.getLong("userId");
        Assert.isTrue(userId != null,"请求参数缺失！");
        UserInfoVo userInfo=usersLogic.getUserByToken(token);
        if(userInfo == null){
            throw new BrillilabException(ResultEnum.USER_NOT_LOGIN);
        }
        LabMember labMember=labMemberService.selectMember(userInfo.getLabId(),userId);
        Assert.isTrue(labMember != null,"无法查询其他实验室成员!");
        return ResponseVo.success(usersLogic.getUserById(userId));
    }

    /**
     * Token获取用户信息 +
     *
     * @return
     */
    @GetMapping("/info")
    public ResponseVo getUserByToken(Long userId,@RequestHeader(name=StarterConstant.TOKEN) String token) {
        Assert.isTrue(StringUtils.isNotBlank(token),"请求参数缺失！");
        if(userId!=null){
            UserInfoVo userInfo=usersLogic.getUserByToken(token);
            if(userInfo == null){
                throw new BrillilabException(ResultEnum.USER_NOT_LOGIN);
            }
            LabMember labMember=labMemberService.selectMember(userInfo.getLabId(),userId);
            Assert.isTrue(labMember != null,"无法查询其他实验室成员!");
            return ResponseVo.success(usersLogic.getUserById(userId));
        }else {
            // 通过token获取用户
            UserInfoVo infoVo=usersLogic.getUserByToken(token);
            if(infoVo == null){
                throw new BrillilabException(ResultEnum.USER_NOT_LOGIN);
            }
            UserInfoVo userInfo=usersLogic.getUserInfo(infoVo.getLabId(),infoVo.getId());
            if(userInfo == null){
                userInfo=usersLogic.getUserById(infoVo.getId());
            }
            userInfo.setToken(token);
            userInfo.setLabNum(infoVo.getLabNum());
            usersLogic.resetUserCache(token,userInfo);
            return ResponseVo.success(userInfo);
        }
    }

    /**
     * 用户切换实验室
     * {\"labId\":\"要切换到的实验室ID 必填 L\"}
     */
    @PostMapping("/changeLab")
    public ResponseVo changeLab(@RequestBody JSONObject json,@RequestHeader(name=StarterConstant.TOKEN) String token) {

        Long labId=json.getLong("labId");

        if(StringUtils.isBlank(token)){
            return ResponseVo.failure(ResultEnum.FAILURE,"token缺失！",null);
        }
        if(labId == null){
            return ResponseVo.failure(ResultEnum.REQUEST_PARAM_LACK);
        }

        //切换实验室的业务
        UserInfoVo userInfo=usersLogic.changeLab(token,labId);

        return ResponseVo.success(userInfo);
    }

    /**
     * 用户登出
     * <p>
     * 根据Token删除用户信息
     */
    @PostMapping("/logout")
    public ResponseVo logout(@RequestHeader(name=StarterConstant.TOKEN) String token) {
        if(StringUtils.isBlank(token)){
            return ResponseVo.failure(ResultEnum.FAILURE,"token缺失！",null);
        }

        usersLogic.userLogout(token);

        return ResponseVo.success();
    }

    /**
     * 用户注册发送短信验证码 {\"mobile\":\"手机号 必填 S\"}
     *
     * @return
     */
    @PostMapping("/rgSmsCode")
    public ResponseVo sendRegisterVerifyCode(@RequestBody JSONObject json, @RequestHeader(name=StarterConstant.TOKEN) String token) {

        Long labId = json.getLong("labId");
        String mobile = json.getString("mobile");

        if (!StringUtils.isBlank(token)) {
            UserInfoVo userInfoVo = usersLogic.getUserByToken(token);
            labId = userInfoVo.getLabId();
        }

        Assert.isTrue(StringUtils.isNotBlank(mobile), "请输入手机号！");
        Assert.isTrue(ValidatorUtil.isPhone(mobile), "请输入正确的手机号！");

        // 判断用户是否存在
        ResponseVo failure = usersLogic.getExistUserOrExistMemberResult(labId, mobile);
        if (failure != null)
            return failure;

        boolean b = messageLogic.sendVerifyCode(mobile);
        Assert.isTrue(b, "短信验证失败！");
        Users user = usersLogic.getUser(mobile);
        return ResponseVo.success(user,"验证码已发送,请注意查收");
    }

    /**
     * 新用户注册
     *
     * @param registerReq
     * @return
     */
    @AspectLog
    @PostMapping("/register")
    public ResponseVo register(@RequestBody UserRegisterReq registerReq) {

        Assert.notNull(registerReq,"请求参数缺失！");
        Assert.notNull(registerReq.getLabId(),"labId 缺失！");
        Assert.isTrue(StringUtils.isNotBlank(registerReq.getMobile()),"请输入手机号！");
        Assert.isTrue(StringUtils.isNotBlank(registerReq.getSmsCode()),"请输入验证码！");
        Assert.isTrue(ValidatorUtil.isPhone(registerReq.getMobile()),"请输入正确的手机号码！");
        Assert.isTrue(StringUtils.isNotBlank(registerReq.getUserName()),"请填写姓名！");
        Assert.isTrue(registerReq.getUserName().length() <= 20,"姓名长度不可超过20个字符！");
        Assert.isTrue(StringUtils.isNotBlank(registerReq.getPassword()),"请输入密码！");
        Assert.isTrue(ValidatorUtil.isPassword(registerReq.getPassword()),"密码为6-20个字符，由字母，数字组成！");
        Assert.isTrue(StringUtils.isNotBlank(registerReq.getCertificateNo()),"请填写证件号！");
        Assert.isTrue(ValidatorUtil.isCertificateNo(registerReq.getCertificateNo()),"证件号为6-20个字符，包括字母或数字！");
        Assert.isTrue(StringUtils.isNotBlank(registerReq.getDuty()),"请选择职称！");

        // 判断用户是否存在
        ResponseVo failure=usersLogic.getExistUserOrExistMemberResult(registerReq.getLabId(),registerReq.getMobile());
        if(failure != null) return failure;

        // 校验短信验证码
        if(!StringUtils.isBlank(registerReq.getSmsCode())){
            boolean b=messageLogic.checkVerifyCode(registerReq.getMobile(),registerReq.getSmsCode());
            Assert.isTrue(b,"验证码错误！");
        }

        //注册逻辑
        Users result=usersLogic.userRegister(registerReq.getLabId(),
                registerReq.getMobile().trim(),registerReq.getPassword().trim(),
                registerReq.getUserName().trim(),registerReq.getDuty(),registerReq.getEntryDate(),registerReq.getCertificateNo());


        UserInfoVo userInfo=new UserInfoVo();
        BeanUtils.copyProperties(result,userInfo);
        return ResponseVo.success(userInfo);
    }

    /**
     * 用户绑定实验室
     * <p>
     * {\"labId\":\"实验室ID 必填 L\",\"mobile\":\"用户手机号 必填 S\",\"password\":\"用户密码 必填 S\"}
     *
     * @return
     */
    @AspectLog
    @PostMapping("/bindLab")
    public ResponseVo bindLab(@RequestBody UserBindLab req) {
        //TODO 卡号

        Assert.notNull(req,"请求参数缺失！");
        Assert.notNull(req.getLabId(),"labId 缺失！");
        Assert.isTrue(StringUtils.isNotBlank(req.getMobile()),"请输入手机号！");
        Assert.isTrue(ValidatorUtil.isPhone(req.getMobile()),"请输入正确的手机号码！");
        Assert.isTrue(StringUtils.isNotBlank(req.getPassword()),"请输入密码！");
        Assert.isTrue(StringUtils.isNotBlank(req.getDuty()),"请选择职称！");

        // 判断用户是否存在
        labMemberLogic.joinLabBecomeMember(req);

        return ResponseVo.success();
    }

    /**
     * 修改用户安全信息身份验证
     *
     * @param uavReq
     * @return
     */
    @AspectLog
    @PostMapping("/identityValid")
    public ResponseVo identityValidation(@RequestBody UserAuthValidateReq uavReq,@RequestHeader(name=StarterConstant.TOKEN) String token) {

        //参数校验
        Assert.isTrue(!StringUtils.isBlank(token) && uavReq != null && uavReq.getType() != null && StringUtils.isNotBlank(uavReq.getCertificateNo()),"请求参数缺失！");

        UserInfoVo userInfo=usersLogic.getUserByToken(token);

        usersLogic.validateCertificateNoOrEmail(uavReq,userInfo);

        IdentityValidateResp ivr=usersLogic.doIdentityValidate(uavReq,userInfo);

        return ResponseVo.success(ivr);

    }

    /**
     * 修改用户安全信息
     *
     * @param umReq
     * @return
     */
    @AspectLog
    @PostMapping("/securityModify")
    public ResponseVo securityModify(@RequestBody UserModifyReq umReq,@RequestHeader(name=StarterConstant.TOKEN) String token) {

        //参数校验
        Assert.isTrue(StringUtils.isNotBlank(token) && umReq != null && umReq.getType() != null && StringUtils.isNotBlank(umReq.getValidateId()),"请求参数缺失！");
        UserInfoVo userInfo=usersLogic.getUserByToken(token);

        UserInfoVo userInfoVo=null;
        if(umReq.getType().equals(UserModifyReq.Type.MOBILE.getCode())){
            userInfoVo=usersLogic.modifyMobile(userInfo,umReq.getMobile(),umReq.getValidateCode(),umReq.getValidateId());
        } else if(umReq.getType().equals(UserModifyReq.Type.EMAIL.getCode())){
            userInfoVo=usersLogic.modifyEmail(userInfo,umReq.getEmail(),umReq.getValidateCode(),umReq.getValidateId());
        } else if(umReq.getType().equals(UserModifyReq.Type.PASSWORD.getCode())){
            boolean rs=usersLogic.modifyPasswordWithValidate(userInfo.getId(),umReq.getPassword(),umReq.getValidateId());
            Assert.isTrue(rs,"密码修改失败！");
            userInfoVo=userInfo;
        } else if(umReq.getType().equals(UserModifyReq.Type.SECURITY_PWD.getCode())){
            boolean rs=usersLogic.modifySecurityPwd(userInfo.getId(),umReq.getSecurityPwd(),umReq.getValidateId());
            Assert.isTrue(rs,"数据密码修改失败！");
            userInfoVo=userInfo;
        } else {
            return ResponseVo.failure(ResultEnum.FAILURE,"修改类型不存在！",null);
        }

        //更新缓存信息
        usersLogic.resetUserCache(token,userInfoVo);

        ResponseVo success=ResponseVo.success(userInfo);
        switch (umReq.getType()) {
            case 1:
                success.setMessage("手机号修改成功！");
                break;
            case 2:
                success.setMessage("邮箱修改成功！");
                break;
            case 3:
                success.setMessage("密码修改成功！");
                break;
            case 4:
                success.setMessage("数据密码修改成功！");
                break;
            default:
                break;
        }

        return success;
    }

    /**
     * 用户实验室列表
     *
     * @return
     */
    @RequestMapping(value="/lab/list", method=RequestMethod.GET)
    public ResponseVo labList(@RequestHeader(name=StarterConstant.TOKEN) String token) {
        //获取token获取用户缓存信息
        if(StringUtils.isBlank(token)){
            return ResponseVo.failure(ResultEnum.FAILURE,"token缺失！",null);
        }
        UserInfoVo userInfo=usersLogic.getUserByToken(token);

        if(userInfo == null){
            return ResponseVo.failure(ResultEnum.FAILURE,"用户登录信息失效！",null);
        }

        List<LabInfoVo> labList=usersLogic.getLabList(userInfo.getId());

        return ResponseVo.success(labList);

    }


    /**
     * 忘记密码身份校验
     *
     * @return
     */
    @RequestMapping(value="/forget/identityValid", method=RequestMethod.POST)
    public ResponseVo forgetIdentityValid(@RequestBody JSONObject jsonObject) {

        String mobile=jsonObject.getString("mobile");
        String validateCode=jsonObject.getString("validateCode");
        String certificateNo=jsonObject.getString("certificateNo");

        Assert.isTrue(!StringUtils.isBlank(mobile),"请输入手机号！");
        Assert.isTrue(!StringUtils.isBlank(validateCode),"请输入验证码！");
        Assert.isTrue(!StringUtils.isBlank(certificateNo),"请输入证件号或邮箱！");
        Assert.isTrue(ValidatorUtil.isPhone(mobile),"请输入正确的手机号！");

        Users user=usersLogic.getUser(mobile);
        Assert.isTrue(user != null,"该手机号未注册！");

        //证件号校验
        if(ValidatorUtil.isCertificateNo(certificateNo)){
            Assert.isTrue(user.getCertificateNo().equals(certificateNo),"证件号或邮箱错误！");
        }else if(ValidatorUtil.isEmail(certificateNo)){
            Assert.isTrue(certificateNo.equals(user.getEmail()),"证件号或邮箱错误！");
        }else {
            return ResponseVo.failure("证件号或邮箱错误！");
        }

        //手机验证码校验
        boolean b=usersLogic.validateIdentityByMobile(mobile.trim(),validateCode.trim());
        Assert.isTrue(b,"验证码错误！");

        //生成校验成功缓存信息
        String validateId=CodeUtil.getUUID();
        redisCache.set(KeyProfixConstant.VALIDATE_ID + validateId,user.getId(),120);

        IdentityValidateResp ivr=new IdentityValidateResp();
        ivr.setUserId(user.getId());
        ivr.setValidateId(validateId);
        return ResponseVo.success(ivr);
    }

    /**
     * 忘记密码修改密码
     *
     * @param jsonObject
     * @return
     */
    @RequestMapping(value="/forget/pswModify", method=RequestMethod.POST)
    public ResponseVo forgetModify(@RequestBody JSONObject jsonObject) {

        String password=jsonObject.getString("password");
        String validateId=jsonObject.getString("validateId");

        Assert.isTrue(!StringUtils.isBlank(password),"请输入新密码！");
        Assert.isTrue(ValidatorUtil.isPassword(password),"密码为6-20个字符，由字母，数字组成！");
        Assert.isTrue(!StringUtils.isBlank(validateId),"validateId 缺失！");

        boolean b=usersLogic.forgetPasswordModify(validateId,password);
        Assert.isTrue(b,"密码修改失败！");

        return ResponseVo.success("密码修改成功！");
    }

    /**
     * 一体机找回密码
     *
     * @param req
     * @return
     */
    @RequestMapping(value="/forget", method=RequestMethod.POST)
    public ResponseVo forgetModify(@RequestBody UserModifyReq req) {

        //参数校验
        Assert.isTrue(!StringUtils.isBlank(req.getMobile())
                && !StringUtils.isBlank(req.getValidateCode())
                && !StringUtils.isBlank(req.getPassword()),"请求参数缺失！");

        Assert.isTrue(ValidatorUtil.isPhone(req.getMobile()),"请输入正确的手机号！");

        Users user=usersLogic.getUser(req.getMobile());

        //邮箱 证件号校验
        String certificateNo=StringUtils.trimToNull(req.getCertificateNo());
        if(ValidatorUtil.isCertificateNo(certificateNo)){
            Assert.isTrue(user.getCertificateNo().equals(certificateNo),"证件号或邮箱错误！");
        }
        if(ValidatorUtil.isEmail(certificateNo)){
            Assert.isTrue(certificateNo.equals(user.getEmail()),"证件号或邮箱错误！");
        }

        //短信验证校验
        boolean b=messageLogic.checkVerifyCode(req.getMobile().trim(),req.getValidateCode().trim());
        Assert.isTrue(b,"短信验证码错误或失效！");

        //修改密码
        boolean rs=usersLogic.modifyPassword(user.getId(),req.getPassword().trim());
        Assert.isTrue(rs,"用户密码修改失败！");

        return ResponseVo.success("密码修改成功！");
    }

    /**
     * 修改个人信息
     *
     * @param users
     * @param token
     * @return
     */
    @AspectLog
    @RequestMapping(value="/modify", method=RequestMethod.POST)
    public ResponseVo modify(@RequestBody Users users,@RequestHeader(name=StarterConstant.TOKEN) String token) {

        Assert.isTrue(!StringUtils.isBlank(token) && users != null,"请求参数缺失！");

        //设置值
        UserInfoVo userInfo=usersLogic.getUserByToken(token);

        Users me=usersLogic.getUser(userInfo.getId());

        if(!StringUtils.isBlank(users.getName())){
            Assert.isTrue(users.getName().length() <= 20,"姓名长度不可超过20个字符！");
            me.setName(users.getName());
            userInfo.setName(users.getName());
        }

        if(!StringUtils.isBlank(users.getImageUrl())){
            me.setImageUrl(users.getImageUrl());
            userInfo.setImageUrl(users.getImageUrl());
        }

        if(users.getSex() != null){
            me.setSex(users.getSex());
            userInfo.setSex(users.getSex());
        }

        //更新
        boolean rs=usersService.updateById(me);
        Assert.isTrue(rs,"用户信息修改失败！");

        //重设缓存信息
        usersLogic.resetUserCache(token,userInfo);

        return ResponseVo.success(userInfo);
    }

}
